using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Trentacular.SharePoint.Features.Util
{
    public static class CollectionUtils
    {
        public static int Compare(object o1, object o2, params string[] propertyNames)
        {
            if (propertyNames == null || propertyNames.Length == 0)
                return 0;

            string propertyName = propertyNames[0];
            string[] secondaryPropertyNames = new string[propertyNames.Length - 1];
            for (int i = 1; i < propertyNames.Length; i++)
            {
                secondaryPropertyNames[i - 1] = propertyNames[i];
            }

            PropertyInfo property = o1.GetType().GetProperty(propertyName);
            IComparable value1 = property.GetValue(o1, null) as IComparable;
            IComparable value2 = property.GetValue(o2, null) as IComparable;

            int result = Compare(value1, value2);
            if (result != 0)
                return result;

            return Compare(o1, o2, secondaryPropertyNames);
        }

        public static int Compare(IComparable value1, IComparable value2)
        {
            if (value1 == null && value2 == null)
                return 0;

            if (value1 == null) return -1;
            if (value2 == null) return 1;

            if (value1 == value2)
                return 0;

            return value1.CompareTo(value2);
        }

        public class PropertyComparer<T> : IComparer<T>
        {
            public string[] PropertyNames { get; private set; }
            public bool Reverse { get; private set; }

            public PropertyComparer(bool reverse, params string[] propertyNames)
            {
                Reverse = reverse;
                PropertyNames = propertyNames;
            }

            #region IComparer<T> Members

            public virtual int Compare(T x, T y)
            {
                int compare = CollectionUtils.Compare(x, y, PropertyNames);
                if (Reverse) return -compare;
                return compare;
            }

            #endregion
        }

        public class PropertyComparer : PropertyComparer<object>, IComparer
        {
            public PropertyComparer(bool reverse, params string[] propertyNames)
                : base(reverse, propertyNames) { }
        }

        public static IEnumerable Sort(this IEnumerable collection, bool reverse, params string[] propertyNames)
        {
            if (collection is ArrayList)
            {
                ArrayListSort((ArrayList)collection, reverse, propertyNames);
                return collection;
            }

            var collectionType = collection.GetType();
            if (collectionType.IsGenericType && collectionType.GetGenericTypeDefinition().Equals(typeof(List<>)))
            {
                var genericItemType = collectionType.GetGenericArguments()[0];
                typeof(CollectionUtils).GetGenericMethod("GenericSort")
                    .MakeGenericMethod(genericItemType)
                    .Invoke(null, new object[] { collection, reverse, propertyNames });
                return collection;
            }

            var list = collection.Cast<object>().ToList();
            GenericSort(list, reverse, propertyNames);
            return list;
        }

        private static void ArrayListSort(ArrayList collection, bool reverse, params string[] propertyNames)
        {
            collection.Sort(new PropertyComparer(reverse, propertyNames));
        }

        private static void GenericSort<T>(List<T> collection, bool reverse, params string[] propertyNames)
        {
            collection.Sort(new PropertyComparer<T>(reverse, propertyNames));
        }
	}
}
